package com.demos.cqp.dnt.dao;

import java.util.ArrayList;
import java.util.List;

import javax.annotation.Resource;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.ParameterExpression;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.transaction.HeuristicMixedException;
import javax.transaction.HeuristicRollbackException;
import javax.transaction.NotSupportedException;
import javax.transaction.RollbackException;
import javax.transaction.SystemException;
import javax.transaction.UserTransaction;

import com.demos.cqp.dnt.domain.DVD;
import com.demos.cqp.dnt.domain.Utilisateur;

@DAO
// @Transactional
public class JPADvdDAO implements DvdthequeDAO {

	// Contexte de type transactionnel
	@PersistenceContext(unitName = "myPU")
	private EntityManager em;

	@Resource
	private UserTransaction ut;

	@Override
	public void enregistrer(DVD dvd, Utilisateur user) throws DAOException {

		try {
			ut.begin();
		} catch (NotSupportedException e) {
			e.printStackTrace();
		} catch (SystemException e) {
			e.printStackTrace();
		}
		em.merge(user);
		dvd.setProprietaire(user);
		// Associe l'objet au contexte
		// ...On pousse l'objet dans le contexte de persistance ...
		em.persist(dvd);
		// ... L'objet est bien devenu persistant m�me s'il n'est pas r�ellement
		// persist�

		// insert, update, delete des objets dans le contexte
		// em.flush();

		try {
			ut.commit();
		} catch (SecurityException e) {
			e.printStackTrace();
		} catch (IllegalStateException e) {
			e.printStackTrace();
		} catch (RollbackException e) {
			e.printStackTrace();
		} catch (HeuristicMixedException e) {
			e.printStackTrace();
		} catch (HeuristicRollbackException e) {
			e.printStackTrace();
		} catch (SystemException e) {
			e.printStackTrace();
		}

	}

	@Override
	public void mettreAJour(DVD dvd) throws DAOException {
		try {
			ut.begin();
		} catch (NotSupportedException e) {
			e.printStackTrace();
		} catch (SystemException e) {
			e.printStackTrace();
		}
		// Associe l'objet au contexte
		// ...On pousse l'objet dans le contexte de persistance ...
		em.merge(dvd);
		// ... L'objet est bien devenu persistant m�me s'il n'est pas r�ellement
		// persist�

		// insert, update, delete des objets dans le contexte
		// em.flush();

		try {
			ut.commit();
		} catch (SecurityException e) {
			e.printStackTrace();
		} catch (IllegalStateException e) {
			e.printStackTrace();
		} catch (RollbackException e) {
			e.printStackTrace();
		} catch (HeuristicMixedException e) {
			e.printStackTrace();
		} catch (HeuristicRollbackException e) {
			e.printStackTrace();
		} catch (SystemException e) {
			e.printStackTrace();
		}

	}

	@Override
	public DVD consulter(long dvdId) throws DAOException {
		return em.find(DVD.class, dvdId);
	}

	@Override
	public List<DVD> rechercher(String titre, Utilisateur user) throws DAOException {

		CriteriaBuilder builder = em.getCriteriaBuilder();
		CriteriaQuery<DVD> cQuery = builder.createQuery(DVD.class);
		Root<DVD> emp = cQuery.from(DVD.class);
		cQuery.select(emp);
		List<Predicate> criteria = new ArrayList<Predicate>();

		if (user != null) {
			ParameterExpression<Utilisateur> p = builder.parameter(Utilisateur.class,
					"proprietaire");
			criteria.add(builder.equal(emp.get("proprietaire"), p));
		}

		if (titre != null) {
			ParameterExpression<String> p = builder.parameter(String.class,
					"titre");
			criteria.add(builder.like(emp.<String> get("titre"), p));
		}

		if (criteria.size() == 0) {
			throw new RuntimeException("pas de critere");
		} else if (criteria.size() == 1) {
			cQuery.where(criteria.get(0));
			System.out.println("Un seul crit�re" + criteria);
		} else {
			System.out.println("Plusieurs crit�res " + criteria);
			cQuery.where(builder.and(criteria.toArray(new Predicate[0])));
		}

		TypedQuery<DVD> tQuery = em.createQuery(cQuery);

		if (user != null) {
			tQuery.setParameter("proprietaire", user);
		}

		if (titre != null) {
			tQuery.setParameter("titre", "%" + titre + "%");
		}

		return tQuery.getResultList();

	}

	@Override
	public List<DVD> charger(Utilisateur user) throws DAOException {

		CriteriaBuilder builder = em.getCriteriaBuilder();
		CriteriaQuery<DVD> cQuery = builder.createQuery(DVD.class);
		Root<DVD> emp = cQuery.from(DVD.class);
		cQuery.select(emp);
		List<Predicate> criteria = new ArrayList<Predicate>();

		if (user != null) {
			ParameterExpression<Utilisateur> p = builder.parameter(Utilisateur.class,
					"proprietaire");
			criteria.add(builder.equal(emp.get("proprietaire"), p));
		}

		if (criteria.size() == 0) {
			throw new RuntimeException("pas de critere");
		} else if (criteria.size() == 1) {
			cQuery.where(criteria.get(0));
			System.out.println("Un seul crit�re" + criteria);
		} else {
			System.out.println("Plusieurs crit�res " + criteria);
			cQuery.where(builder.and(criteria.toArray(new Predicate[0])));
		}

		TypedQuery<DVD> tQuery = em.createQuery(cQuery);

		if (user != null) {
			tQuery.setParameter("proprietaire", user);
		}

		return tQuery.getResultList();
	}

	@Override
	public void delete(long idDVD) throws DAOException {
		try {
			ut.begin();
		} catch (NotSupportedException e) {
			e.printStackTrace();
		} catch (SystemException e) {
			e.printStackTrace();
		}
		DVD dvd = em.find(DVD.class, idDVD);
		em.remove(dvd);

		try {
			ut.commit();
		} catch (SecurityException e) {
			e.printStackTrace();
		} catch (IllegalStateException e) {
			e.printStackTrace();
		} catch (RollbackException e) {
			e.printStackTrace();
		} catch (HeuristicMixedException e) {
			e.printStackTrace();
		} catch (HeuristicRollbackException e) {
			e.printStackTrace();
		} catch (SystemException e) {
			e.printStackTrace();
		}

	}

}
